﻿
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;

using Swaf;
using Swaf.BizObj;
using Swaf.BizRule;
using Swaf.Call;
using Swaf.Container;
using Swaf.Scheduler;
using Swaf.Security;

namespace GeoByte
{
    public class GeoByteBizRules : Syrinx.BaseBizRules
    {
        public void validateIpAddressesGeoInfo(IApplication app, IRuleContext context, string appName, string ipAddresses)
        {
            string[] ips = ipAddresses.Split(',');
            FlexiMap m = new FlexiMap();
            m.put("requestingApp", appName);
            foreach (string ip in ips)
            {
                m.put("ipAddress", ip);
                IBizObj info = app.appData.get("DA.GeoByte.GetAppIpReq", m) as IBizObj;
                if (info == null)
                    app.appData.get("DA.GeoByte.AddAppIpReq", m);
                info = app.appData.get("DA.GeoByte.IsIpKnown", m) as IBizObj;
                if (info == null)
                    app.appData.get("DA.GeoByte.AddIp", m);
            }
        }

        public void scheduledCheckForUnvalidatedUserLoginIps(IApplication app, IRuleContext context)
        {
            IBizObjList unvalidatedIps = app.appData.get("DA.GeoByte.GetUnvalidatedUserLogins", context) as IBizObjList;
            StringBuilder buff = new StringBuilder();
            foreach (IBizObj unv in unvalidatedIps)
                buff.Append(unv.get("userIPAddresses")).Append(",");
            if(buff.Length > 0)
                app.ruleMgr.execute("validateIpAddressesGeoInfo", app.appData.get("AppName"), buff.ToString(0, buff.Length - 1));
            foreach (IBizObj unv in unvalidatedIps)
                app.appData.put("DA.GeoByte.UpdateUnvalidatedLoginToValidated", unv);
        }

        public void scheduledCheckForIpAddressGeoInfo(IApplication app, IRuleContext context)
        {
            IBizObj config = loadConfig(app);
            int freePerIp = (int)config.get("freePerIp");
            DateTime n = DateTime.UtcNow;
            DateTime lastHour = (DateTime)config.get("runtime.lastHourWindow");
            DateTime curHour = new DateTime(n.Year, n.Month, n.Day, n.Hour, 0, 0);
            string[] localIps = config.get("localIP").ToString().Split(',');
            IBizObjList localIpRequests = (IBizObjList)config.get("runtime.checks");
            if (curHour != lastHour)
            {
                localIpRequests = app.bizObjMgr.createList("GeoByteLocalIpRuntimeInfo");
                config.put("runtime.checks", localIpRequests);
                localIpRequests.put("lastHourWindow", curHour);
            }
            Dictionary<string, Syrinx.IPBinder> m_binders = new Dictionary<string, Syrinx.IPBinder>();
            IBizObjList ipToValidate = app.appData.get("DA.GeoByte.GetUnvalidatedIps", context) as IBizObjList;
            foreach (IBizObj ip in ipToValidate)
            {
                Syrinx.IPBinder binder = null;
                foreach (string localip in localIps)
                {
                    IBizObj localIpCount = localIpRequests.search("localIP", localip);
                    if (localIpCount == null)
                    {
                        localIpCount = localIpRequests.insertNew();
                        localIpCount.put("localIP", localip);
                    }
                    int numChecked = (int)localIpCount.get("numChecked");
                    if (numChecked < freePerIp)
                    {
                        if (m_binders.ContainsKey(localip))
                            binder = m_binders[localip];
                        else
                            m_binders[localip] = binder = new Syrinx.IPBinder(localip);

                        localIpCount.put("numChecked", numChecked+1);
                        break;
                    }
                }
                if (binder != null)
                {
                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
                        string.Format("http://www.geobytes.com/IpLocator.htm?GetLocation&template=xml.txt&IpAddress={0}",
                        ip.get("ipAddress")));
                    req.ServicePoint.BindIPEndPointDelegate = new BindIPEndPoint(binder.BindIPEndPointCallback);

                    HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                    Stream str = resp.GetResponseStream();
                    string respXml = new StreamReader(str).ReadToEnd();
                    resp.Close();
                    try
                    {
                        XmlDocument doc = new XmlDocument();
                        doc.LoadXml(respXml);
                        XmlElement el = doc.DocumentElement;
                        if (!Cmn.IsEmpty(el.SelectSingleNode("countryid").InnerText))
                        {
                            ip.put("isKnown", true);
                            foreach (XmlNode nd in el.ChildNodes)
                            {
                                string name = nd.Name;
                                if (name.CompareTo("code") == 0)
                                    name = "regionCode";
                                string val = nd.InnerText;
                                if (nd.Name.CompareTo("timezone") == 0 && !Cmn.IsEmpty(val))
                                    val = val.Substring(0, val.IndexOf(':'));
                                IBizObjField fld = ip.type.getField(name, false);
                                if (fld != null)
                                    ip.putValidated(fld.Name, val);
                            }
                        }
                        ip.put("timeValidated", n);
                        app.appData.get("DA.GeoByte.UpdateIp", ip);
                    }
                    catch (Exception)
                    {
                    }
                }
            }
        }

        public IBizObj getIpAddressGeoInfo(IApplication app, IRuleContext context, string appName, string ipAddress)
        {
            IBizObj addrInfo = null;

            return addrInfo;
        }

        protected IBizObj loadConfig(IApplication app)
        {
            //TODO: Read this from an external location like a Catalog, db row or file.
            return app.appData.get("GeoByteConfig") as IBizObj;
        }
    }
}
